/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_LOSHU_H__
#define __CR_LOSHU_H__

#include <Windows.h>
#include <crdebug.h>

// byte margins to four side of border, every is limited in a byte
#define MAKEBYTEMARGIN(l,r,t,b)  \
	(((DWORD)(BYTE)l)<<24 | ((DWORD)(BYTE)r)<<16 | ((DWORD)(BYTE)t)<<8 | ((DWORD)(BYTE)b))
#define BYTEMARGINL(m) ((BYTE)((DWORD)m)>>24)
#define BYTEMARGINR(m) ((BYTE)((DWORD)m)>>16)
#define BYTEMARGINT(m) ((BYTE)((DWORD)m)>>8)
#define BYTEMARGINB(m) ((BYTE)((DWORD)m))

// word margins to four side of border, every is limited in a word
#define MAKEWORDMARGINX(l,r)  (((DWORD)(WORD)l)<<16 | ((DWORD)(WORD)r)))
#define MAKEWORDMARGINY(t,b)  (((DWORD)(BYTE)t)<<16 | ((DWORD)(BYTE)b)))
#define WORDMARGINL(mx) ((BYTE)((DWORD)mx)>>16)
#define WORDMARGINT(mx) ((BYTE)((DWORD)mx))
#define WORDMARGINR(my) ((BYTE)((DWORD)my)>>16)
#define WORDMARGINB(my) ((BYTE)((DWORD)my))

typedef RECT LOSHUMARGIN;  // distances to four side of external box
typedef SIZE LOSHUSIZE[3]; // size of row[0]col[0] row[1]col[1] row[2]col[2]
typedef POINT LOSHUPOINT[3]; // point of row[0]col[0] row[1]col[1] row[2]col[2]

inline BOOL IsValidLoshu(const LOSHUMARGIN& margin, int cx, int cy)
{
	if (cx < 0 || cy < 0)
		return FALSE;
	if (margin.left < 0 || margin.top < 0 || margin.right < 0 || margin.bottom <  0)
		return FALSE;
	if (cx < margin.left + margin.right || cy < margin.top + margin.bottom)
		return FALSE;
	return TRUE;
}

inline void CalcMargin(LOSHUMARGIN& margin, const RECT& box, const RECT& center)
{
	margin.left = center.left - box.left;
	margin.right = box.right - center.right;
	margin.top = center.top - box.top;
	margin.bottom = box.bottom - center.bottom;
}

inline void GetCenterRect(RECT& center, const LOSHUMARGIN& margin, const RECT& box)
{
	center.left = box.left + margin.left;
	center.top = box.top + margin.top;
	center.right = box.right - margin.right;
	center.bottom = box.bottom - margin.bottom;
}

inline void GetLoshuSize(LOSHUSIZE& loshu, const LOSHUMARGIN& margin, int cx, int cy)
{
	loshu[0].cx = margin.left;
	loshu[0].cy = margin.top;
	loshu[1].cx = cx - margin.left - margin.right;
	loshu[1].cy = cy - margin.top - margin.bottom;
	loshu[2].cx = margin.right;
	loshu[2].cy = margin.bottom;
}

inline void GetLoshuPoint(LOSHUPOINT& loshu, const LOSHUSIZE& size, int x, int y)
{
	loshu[0].x = x;
	loshu[0].y = y;
	loshu[1].x = loshu[0].x + size[0].cx;
	loshu[1].y = loshu[0].y + size[0].cy;
	loshu[2].x = loshu[1].x + size[1].cx;
	loshu[2].y = loshu[1].y + size[1].cy;
}

struct LOSHUCONTEXT
{
	HDC hdcDst;
	HDC hdcSrc;
	LOSHUPOINT ptDst;
	LOSHUPOINT ptSrc;
	LOSHUSIZE czDst;
	LOSHUSIZE czSrc;
	DWORD dwRop;
};

inline BOOL InitLoshuContext(LOSHUCONTEXT& ctx, 
	HDC hdcDst, int xDst, int yDst, int cxDst, int cyDst,
	HDC hdcSrc, int xSrc, int ySrc, int cxSrc, int cySrc,
	const LOSHUMARGIN& margin, DWORD dwRop)
{
	ZeroMemory(&ctx, sizeof(ctx));
	if (!IsValidLoshu(margin, cxDst, cyDst)
		|| !IsValidLoshu(margin, cxSrc, cySrc))
		return FALSE;
	ctx.dwRop = dwRop;
	ctx.hdcDst = hdcDst;
	ctx.hdcSrc = hdcSrc;
	GetLoshuSize(ctx.czDst, margin, cxDst, cyDst);
	GetLoshuSize(ctx.czSrc, margin, cxSrc, cySrc);
	GetLoshuPoint(ctx.ptDst, ctx.czDst, xDst, yDst);
	GetLoshuPoint(ctx.ptSrc, ctx.czSrc, xSrc, ySrc);
	return TRUE;
};

inline BOOL LoshuBltBlock(const LOSHUCONTEXT& ctx, int row, int col)
{
	ASSERT((unsigned)row <= 2 && (unsigned)col <= 2);
	// if ctx.ftn.AlphaFormat is 0, the AlphaBlend do as StretchBlt
	// if the size of source is the same as of destination, then do as BitBlt
	return StretchBlt(
		ctx.hdcDst,
		ctx.ptDst[col].x,
		ctx.ptDst[row].y,
		ctx.czDst[col].cx,
		ctx.czDst[row].cy,
		ctx.hdcSrc,
		ctx.ptSrc[col].x,
		ctx.ptSrc[row].y,
		ctx.czSrc[col].cx,
		ctx.czSrc[row].cy,
		ctx.dwRop);
}

inline BOOL LoshuBltCentral(const LOSHUCONTEXT& ctx)
{
	//the block[1,1]
	return LoshuBltBlock(ctx, 1, 1);
}

inline BOOL LoshuBltHollow(const LOSHUCONTEXT& ctx)
{
	//without the block[1,1]
	return LoshuBltBlock(ctx, 0, 1) 
		&& LoshuBltBlock(ctx, 1, 0)
		&& LoshuBltBlock(ctx, 1, 2)
		&& LoshuBltBlock(ctx, 2, 1)
		&& LoshuBltBlock(ctx, 0, 0)
		&& LoshuBltBlock(ctx, 0, 2)
		&& LoshuBltBlock(ctx, 2, 0)
		&& LoshuBltBlock(ctx, 2, 2);
}

#endif //__CR_LOSHU_H__